"""
Main game class integrating all components
"""
import pygame
import sys
import os
import random
from typing import List
from utils.config import WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE, FPS, ENEMY_COUNT
from utils.constants import GameState, TankType, BulletType
from utils.sound_manager import get_sound_manager
from utils.effects import get_effects_manager

# Import game components
from .player import PlayerTank
from .enemy import EnemyTank
from .bullet import Bullet
from .map import GameMap
from .collision import CollisionDetector
from .score_system import ScoreSystem
from .level_manager import LevelManager
from .state_manager import GameStateManager
from .power_up import get_power_up_manager


class TankGame:
    """Main game class integrating all components"""
    
    def __init__(self):
        """Initialize the game"""
        pygame.init()
        pygame.mixer.init()
        
        # Create game window
        self.screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
        pygame.display.set_caption(WINDOW_TITLE)
        
        # Game clock
        self.clock = pygame.time.Clock()
        
        # Game state
        self.state = GameState.MENU
        self.running = True
        
        # Initialize game components
        self.game_map = GameMap()
        self.collision_detector = CollisionDetector(self.game_map)
        
        # Game systems
        self.score_system = ScoreSystem()
        self.level_manager = LevelManager()
        self.state_manager = GameStateManager()
        
        # Audio and visual effects
        self.sound_manager = get_sound_manager()
        self.effects_manager = get_effects_manager()
        
        # Menu state
        self.menu_selection = 0  # For main menu
        self.game_over_selection = 0  # For game over menu
        self.max_menu_items = 2  # Start Game, Quit
        self.max_game_over_items = 3  # Restart, Main Menu, Quit
        
        # Game objects
        self.player = None
        self.enemies: List[EnemyTank] = []
        self.bullets: List[Bullet] = []
        
        # Power-up system
        self.power_up_manager = get_power_up_manager()
        
        # Initialize game
        self._initialize_game()
        
        # Start menu music
        self.sound_manager.play_music('menu_music.ogg', loops=-1)
    
    def _initialize_game(self):
        """Initialize game objects"""
        # Get current level settings
        level_settings = self.level_manager.get_level_settings()
        map_settings = self.level_manager.get_map_settings_for_level()
        
        # Generate map with level-specific settings
        self.game_map.generate_random_map(
            brick_density=map_settings['brick_density'],
            steel_density=map_settings['steel_density']
        )
        
        # Validate and fix tile properties (prevent invisible collision bugs)
        self.game_map.validate_and_fix_tiles()
        
        # Get enemy count and types for current level
        enemy_count = self.level_manager.get_enemy_count_for_level()
        enemy_types = self.level_manager.generate_enemy_list_for_level()
        
        # Get spawn positions
        spawn_positions = self.game_map.get_spawn_positions(enemy_count + 1)
        
        if spawn_positions:
            # Create player tank
            player_pos = spawn_positions[0]
            self.player = PlayerTank(player_pos[0], player_pos[1])
            
            # Create enemy tanks
            self.enemies = []
            for i in range(1, min(len(spawn_positions), enemy_count + 1)):
                enemy_pos = spawn_positions[i]
                enemy_type = enemy_types[i - 1] if i - 1 < len(enemy_types) else TankType.ENEMY_BASIC
                
                # Determine difficulty based on level
                difficulty = "normal"
                if self.level_manager.get_current_level() > 10:
                    difficulty = "hard"
                elif self.level_manager.get_current_level() <= 3:
                    difficulty = "easy"
                
                enemy = EnemyTank(enemy_pos[0], enemy_pos[1], enemy_type, difficulty)
                self.enemies.append(enemy)
    
    def handle_events(self):
        """Handle pygame events"""
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.running = False
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    if self.state == GameState.PLAYING:
                        self.sound_manager.pause_music()
                        self.state = GameState.PAUSED
                    elif self.state == GameState.PAUSED:
                        self.sound_manager.unpause_music()
                        self.state = GameState.PLAYING
                    else:
                        self.running = False
                        
                # Menu navigation
                elif event.key in (pygame.K_UP, pygame.K_w):
                    if self.state == GameState.MENU:
                        self.menu_selection = (self.menu_selection - 1) % self.max_menu_items
                        self.sound_manager.play_sound('menu_select')
                    elif self.state == GameState.GAME_OVER:
                        self.game_over_selection = (self.game_over_selection - 1) % self.max_game_over_items
                        self.sound_manager.play_sound('menu_select')
                        
                elif event.key in (pygame.K_DOWN, pygame.K_s):
                    if self.state == GameState.MENU:
                        self.menu_selection = (self.menu_selection + 1) % self.max_menu_items
                        self.sound_manager.play_sound('menu_select')
                    elif self.state == GameState.GAME_OVER:
                        self.game_over_selection = (self.game_over_selection + 1) % self.max_game_over_items
                        self.sound_manager.play_sound('menu_select')
                        
                # Confirm selection
                elif event.key == pygame.K_SPACE or event.key == pygame.K_RETURN:
                    if self.state == GameState.MENU:
                        if self.menu_selection == 0:  # Start Game
                            print("Starting game from menu...")
                            self.sound_manager.play_sound('menu_select')
                            self.sound_manager.stop_music(fade_ms=500)
                            self.state = GameState.PLAYING
                            self.sound_manager.play_music('game_music.ogg', loops=-1, fade_ms=1000)
                            print(f"Game state changed to PLAYING. Player alive: {self.player.is_alive() if self.player else 'No player'}")
                        elif self.menu_selection == 1:  # Quit
                            self.running = False
                            
                    elif self.state == GameState.GAME_OVER:
                        if self.game_over_selection == 0:  # Restart
                            self.sound_manager.play_sound('menu_select')
                            self.restart_game()
                        elif self.game_over_selection == 1:  # Main Menu
                            self.sound_manager.play_sound('menu_select')
                            self.return_to_menu()
                        elif self.game_over_selection == 2:  # Quit
                            self.running = False
                            
                elif event.key == pygame.K_r:
                    if self.state == GameState.PLAYING:
                        self.restart_game()
    
    def update(self):
        """Update game logic"""
        if self.state == GameState.PLAYING:
            self._update_game()
        elif self.state == GameState.MENU:
            # Menu logic
            pass
        elif self.state == GameState.PAUSED:
            # Pause logic
            pass
        elif self.state == GameState.GAME_OVER:
            # Game over logic
            pass
    
    def _update_game(self):
        """Update game objects and logic"""
        dt = self.clock.get_time() / 1000.0  # Convert to seconds
        current_time = pygame.time.get_ticks()
        
        # Handle player input
        keys = pygame.key.get_pressed()
        if self.player and self.player.is_alive():
            should_shoot = self.player.handle_input(keys, current_time)
            if should_shoot:
                self._fire_bullet(self.player)
        
        # Update player tank
        if self.player and self.player.is_alive():
            self.player.update(dt)
            # Resolve player collision with map
            self.collision_detector.resolve_tank_map_collision(self.player)
        
        # Update enemy tanks
        all_tanks = [self.player] + self.enemies if self.player else self.enemies
        for enemy in self.enemies:
            if enemy.is_alive():
                enemy.update(dt)
                should_shoot = enemy.update_ai(dt, self.player, all_tanks, self.game_map)
                if should_shoot:
                    self._fire_bullet(enemy)
                # Resolve enemy collision with map
                self.collision_detector.resolve_tank_map_collision(enemy)
        
        # Update bullets
        self._update_bullets(dt)
        
        # Update power-up system
        if self.player and self.player.is_alive():
            player_rect = self.player.get_collision_rect()
            self.power_up_manager.update(current_time, player_rect)
        
        # Update visual effects
        self.effects_manager.update(dt)
        
        # Check collisions
        self._handle_collisions()
        
        # Check game conditions
        self._check_game_conditions()
    
    def _fire_bullet(self, tank):
        """Fire bullet from tank"""
        if not tank.is_alive():
            return
        
        front_x, front_y = tank.get_front_position()
        owner = "player" if tank.tank_type == TankType.PLAYER else "enemy"
        
        bullet = Bullet(front_x, front_y, tank.direction, BulletType.NORMAL, owner)
        self.bullets.append(bullet)
        
        # Play shoot sound
        self.sound_manager.play_sound('shoot')
    
    def _update_bullets(self, dt: float):
        """Update all bullets"""
        bullets_to_remove = []
        
        for bullet in self.bullets:
            if bullet.update(dt, WINDOW_WIDTH, WINDOW_HEIGHT):
                # Bullet is still active
                pass
            else:
                # Bullet should be removed
                bullets_to_remove.append(bullet)
        
        # Remove inactive bullets
        for bullet in bullets_to_remove:
            self.bullets.remove(bullet)
    
    def _handle_collisions(self):
        """Handle all collisions"""
        all_tanks = [self.player] + self.enemies if self.player else self.enemies
        
        # Get collision results
        collision_results = self.collision_detector.check_all_collisions(all_tanks, self.bullets)
        
        # Handle bullet-map collisions
        for collision in collision_results['bullet_map_collisions']:
            bullet = collision['bullet']
            # Create bullet impact effect
            self.effects_manager.create_bullet_impact(bullet.x, bullet.y)
            self.sound_manager.play_sound('hit')
            bullet.destroy()
        
        # Handle bullet-tank collisions
        for collision in collision_results['bullet_tank_collisions']:
            bullet = collision['bullet']
            tank = collision['tank']
            damage = collision['damage']
            
            # Create impact effect
            self.effects_manager.create_bullet_impact(bullet.x, bullet.y)
            self.sound_manager.play_sound('hit')
            bullet.destroy()
            
            if tank.take_damage(damage):
                # Tank destroyed - create explosion
                tank_center_x = tank.x + tank.width / 2
                tank_center_y = tank.y + tank.height / 2
                
                if tank.tank_type == TankType.PLAYER:
                    # Player destroyed
                    self.effects_manager.create_explosion(tank_center_x, tank_center_y, "large")
                    self.sound_manager.play_sound('explosion')
                    
                    if self.player.lives > 0:
                        # Respawn player
                        spawn_positions = self.game_map.get_spawn_positions(1)
                        if spawn_positions:
                            self.player.respawn(spawn_positions[0][0], spawn_positions[0][1])
                    else:
                        # Game over
                        self.sound_manager.play_sound('game_over')
                        self.state = GameState.GAME_OVER
                else:
                    # Enemy destroyed
                    explosion_size = "normal" if tank.tank_type == TankType.ENEMY_BASIC else "large"
                    self.effects_manager.create_explosion(tank_center_x, tank_center_y, explosion_size)
                    self.sound_manager.play_sound('enemy_destroyed')
                    
                    points = self.score_system.destroy_enemy(tank.tank_type)
                    self.level_manager.enemy_destroyed(tank.tank_type)
                    
                    # Check if level is complete
                    if self.level_manager.is_level_complete():
                        self.sound_manager.play_sound('level_complete')
                        self._next_level()
        
        # Handle bullet-bullet collisions
        for bullet1, bullet2 in collision_results['bullet_bullet_collisions']:
            # Create small spark effect
            self.effects_manager.create_bullet_impact((bullet1.x + bullet2.x) / 2,
                                                      (bullet1.y + bullet2.y) / 2)
            bullet1.destroy()
            bullet2.destroy()
        
        # Handle boundary collisions
        for obj in collision_results['boundary_collisions']:
            if isinstance(obj, Bullet):
                obj.destroy()
    
    def _check_game_conditions(self):
        """Check win/lose conditions"""
        # Check if player is dead
        if not self.player or not self.player.is_alive():
            self.state = GameState.GAME_OVER
            return
        
        # Check if all enemies are dead
        alive_enemies = [enemy for enemy in self.enemies if enemy.is_alive()]
        if not alive_enemies:
            # Level completed
            self.level += 1
            self._next_level()
    
    def _next_level(self):
        """Start next level"""
        # Clear current level
        self.bullets.clear()
        
        # Advance level
        if not self.level_manager.advance_level():
            # Max level reached - game completed
            self.state = GameState.GAME_OVER
            return
        
        # Get new level settings
        level_settings = self.level_manager.get_level_settings()
        map_settings = self.level_manager.get_map_settings_for_level()
        
        # Generate new map
        self.game_map.generate_random_map(
            brick_density=map_settings['brick_density'],
            steel_density=map_settings['steel_density']
        )
        
        # Get enemy count and types for new level
        enemy_count = self.level_manager.get_enemy_count_for_level()
        enemy_types = self.level_manager.generate_enemy_list_for_level()
        
        # Get new spawn positions
        spawn_positions = self.game_map.get_spawn_positions(enemy_count + 1)
        
        if spawn_positions:
            # Respawn player
            player_pos = spawn_positions[0]
            self.player.respawn(player_pos[0], player_pos[1])
            
            # Create new enemies
            self.enemies = []
            for i in range(1, min(len(spawn_positions), enemy_count + 1)):
                enemy_pos = spawn_positions[i]
                enemy_type = enemy_types[i - 1] if i - 1 < len(enemy_types) else TankType.ENEMY_BASIC
                
                # Determine difficulty based on level
                difficulty = "normal"
                if self.level_manager.get_current_level() > 10:
                    difficulty = "hard"
                elif self.level_manager.get_current_level() <= 3:
                    difficulty = "easy"
                
                enemy = EnemyTank(enemy_pos[0], enemy_pos[1], enemy_type, difficulty)
                self.enemies.append(enemy)
    
    def render(self):
        """Render the game"""
        # Clear screen
        self.screen.fill((0, 0, 0))
        
        if self.state == GameState.MENU:
            self.render_menu()
        elif self.state == GameState.PLAYING:
            self.render_game()
        elif self.state == GameState.PAUSED:
            self.render_game()
            self.render_pause_overlay()
        elif self.state == GameState.GAME_OVER:
            self.render_game_over()
        
        # Update display
        pygame.display.flip()
    
    def render_menu(self):
        """Render main menu"""
        font_title = pygame.font.Font(None, 48)
        font_menu = pygame.font.Font(None, 36)
        
        title_text = font_title.render("Tank Battle Game", True, (255, 255, 255))
        title_rect = title_text.get_rect(center=(WINDOW_WIDTH//2, WINDOW_HEIGHT//2 - 100))
        self.screen.blit(title_text, title_rect)
        
        # Menu options
        menu_options = ["Start Game", "Quit"]
        
        for i, option in enumerate(menu_options):
            # Highlight selected option
            if i == self.menu_selection:
                color = (255, 255, 0)  # Yellow for selected
                text = font_menu.render(f"> {option} <", True, color)
            else:
                color = (255, 255, 255)  # White for unselected
                text = font_menu.render(option, True, color)
            
            text_rect = text.get_rect(center=(WINDOW_WIDTH//2, WINDOW_HEIGHT//2 + i * 50))
            self.screen.blit(text, text_rect)
        
        # Instructions
        font_small = pygame.font.Font(None, 24)
        inst_text = font_small.render("Use UP/DOWN or W/S to navigate, SPACE/ENTER to select", True, (150, 150, 150))
        inst_rect = inst_text.get_rect(center=(WINDOW_WIDTH//2, WINDOW_HEIGHT - 50))
        self.screen.blit(inst_text, inst_rect)
    
    def render_game(self):
        """Render game objects"""
        # Render map
        self.game_map.render(self.screen)
        
        # Render tanks
        if self.player and self.player.is_alive():
            self.player.render(self.screen)
        
        for enemy in self.enemies:
            if enemy.is_alive():
                enemy.render(self.screen)
        
        # Render bullets
        for bullet in self.bullets:
            if bullet.is_active():
                bullet.render(self.screen)
        
        # Render power-ups
        self.power_up_manager.draw(self.screen)
        
        # Render visual effects (explosions, particles)
        self.effects_manager.render(self.screen)
        
        # Render HUD
        self.render_hud()
    
    def render_hud(self):
        """Render heads-up display"""
        font = pygame.font.Font(None, 24)
        
        # Score
        score_text = font.render(f"Score: {self.score_system.get_current_score()}", True, (255, 255, 255))
        self.screen.blit(score_text, (10, 10))
        
        # Level
        level_text = font.render(f"Level: {self.level_manager.get_current_level()}", True, (255, 255, 255))
        self.screen.blit(level_text, (10, 35))
        
        # Lives
        if self.player:
            lives_text = font.render(f"Lives: {self.player.get_lives()}", True, (255, 255, 255))
            self.screen.blit(lives_text, (10, 60))
        
        # Level progress
        progress = self.level_manager.get_level_progress()
        progress_text = font.render(f"Enemies: {progress['enemies_destroyed']}/{progress['required_enemies']}", True, (255, 255, 255))
        self.screen.blit(progress_text, (10, 85))
        
        # Progress bar
        if progress['required_enemies'] > 0:
            bar_width = 200
            bar_height = 10
            bar_x = 10
            bar_y = 110
            
            # Background
            pygame.draw.rect(self.screen, (100, 100, 100), (bar_x, bar_y, bar_width, bar_height))
            
            # Progress
            progress_width = int(bar_width * (progress['progress_percentage'] / 100))
            pygame.draw.rect(self.screen, (0, 255, 0), (bar_x, bar_y, progress_width, bar_height))
        
        # Power-up status
        current_time = pygame.time.get_ticks()
        power_up_y = 135
        
        from utils.constants import PowerUpType
        if self.power_up_manager.has_power_up(PowerUpType.STEEL_BREAKER):
            remaining_time = self.power_up_manager.get_remaining_time(PowerUpType.STEEL_BREAKER, current_time)
            power_up_text = font.render(f"Steel Breaker: {remaining_time}s", True, (255, 255, 0))
            self.screen.blit(power_up_text, (10, power_up_y))
        
        # FPS
        fps_text = font.render(f"FPS: {int(self.clock.get_fps())}", True, (255, 255, 255))
        self.screen.blit(fps_text, (WINDOW_WIDTH - 100, 10))
    
    def render_pause_overlay(self):
        """Render pause overlay"""
        # Semi-transparent overlay
        overlay = pygame.Surface((WINDOW_WIDTH, WINDOW_HEIGHT))
        overlay.set_alpha(128)
        overlay.fill((0, 0, 0))
        self.screen.blit(overlay, (0, 0))
        
        font = pygame.font.Font(None, 36)
        pause_text = font.render("PAUSED - Press ESC to Resume", True, (255, 255, 255))
        pause_rect = pause_text.get_rect(center=(WINDOW_WIDTH//2, WINDOW_HEIGHT//2))
        self.screen.blit(pause_text, pause_rect)
    
    def render_game_over(self):
        """Render game over screen"""
        font_title = pygame.font.Font(None, 48)
        font_normal = pygame.font.Font(None, 32)
        font_menu = pygame.font.Font(None, 36)
        
        # Game over title
        game_over_text = font_title.render("GAME OVER", True, (255, 0, 0))
        game_over_rect = game_over_text.get_rect(center=(WINDOW_WIDTH//2, WINDOW_HEIGHT//2 - 150))
        self.screen.blit(game_over_text, game_over_rect)
        
        # Score and level info
        score_text = font_normal.render(f"Final Score: {self.score_system.get_current_score()}", True, (255, 255, 255))
        score_rect = score_text.get_rect(center=(WINDOW_WIDTH//2, WINDOW_HEIGHT//2 - 90))
        self.screen.blit(score_text, score_rect)
        
        level_summary = self.level_manager.get_level_summary()
        level_text = font_normal.render(f"Level Reached: {level_summary['current_level']}", True, (255, 255, 255))
        level_rect = level_text.get_rect(center=(WINDOW_WIDTH//2, WINDOW_HEIGHT//2 - 50))
        self.screen.blit(level_text, level_rect)
        
        # High score
        if self.score_system.is_high_score():
            high_score_text = font_normal.render("NEW HIGH SCORE!", True, (255, 255, 0))
            high_score_rect = high_score_text.get_rect(center=(WINDOW_WIDTH//2, WINDOW_HEIGHT//2 - 10))
            self.screen.blit(high_score_text, high_score_rect)
        
        # Menu options
        menu_options = ["Restart Game", "Main Menu", "Quit"]
        
        start_y = WINDOW_HEIGHT//2 + 40
        for i, option in enumerate(menu_options):
            # Highlight selected option
            if i == self.game_over_selection:
                color = (255, 255, 0)  # Yellow for selected
                text = font_menu.render(f"> {option} <", True, color)
            else:
                color = (255, 255, 255)  # White for unselected
                text = font_menu.render(option, True, color)
            
            text_rect = text.get_rect(center=(WINDOW_WIDTH//2, start_y + i * 45))
            self.screen.blit(text, text_rect)
        
        # Instructions
        font_small = pygame.font.Font(None, 24)
        inst_text = font_small.render("Use UP/DOWN or W/S to navigate, SPACE/ENTER to select", True, (150, 150, 150))
        inst_rect = inst_text.get_rect(center=(WINDOW_WIDTH//2, WINDOW_HEIGHT - 30))
        self.screen.blit(inst_text, inst_rect)
    
    def restart_game(self):
        """Restart the game"""
        self.state = GameState.PLAYING
        
        # Reset all systems
        self.score_system.reset_score()
        self.level_manager.reset_game()
        
        # Clear game objects
        self.bullets.clear()
        self.enemies.clear()
        
        # Clear power-ups
        self.power_up_manager.clear()
        
        # Clear effects
        self.effects_manager.clear_all()
        
        # Keep game music playing
        if not self.sound_manager.is_music_playing():
            self.sound_manager.play_music('game_music.ogg', loops=-1, fade_ms=1000)
        
        # Reinitialize game
        self._initialize_game()
    
    def return_to_menu(self):
        """Return to main menu"""
        self.state = GameState.MENU
        
        # Reset all systems
        self.score_system.reset_score()
        self.level_manager.reset_game()
        
        # Clear game objects
        self.bullets.clear()
        self.enemies.clear()
        
        # Clear power-ups
        self.power_up_manager.clear()
        
        # Clear effects
        self.effects_manager.clear_all()
        
        # Reset music
        self.sound_manager.stop_music(fade_ms=500)
        self.sound_manager.play_music('menu_music.ogg', loops=-1, fade_ms=1000)
        
        # Reset menu selection
        self.menu_selection = 0
        self.game_over_selection = 0
        
        # Reinitialize game (for when player starts again)
        self._initialize_game()
    
    def run(self):
        """Main game loop"""
        while self.running:
            self.handle_events()
            self.update()
            self.render()
            self.clock.tick(FPS)
        
        pygame.quit()
        sys.exit()


def main():
    """Main function"""
    try:
        game = TankGame()
        game.run()
    except Exception as e:
        print(f"Error running game: {e}")
        pygame.quit()
        sys.exit(1)


if __name__ == "__main__":
    main()